# Manage the database of feeds and feed contents.
import datetime
import time
import sqlite3
import threading
import traceback
import concurrentfuture
import diskmgr
import os
from log import log

database_filename = os.path.join(diskmgr.base, "podcasts.db")
preinit_sql = []

def RowsOf(c):
  return c.fetchall()

def SqliteExecutor():
  global sqlite_executor
  return sqlite_executor

def SqliteConn():
  global sqlite_conn
  return sqlite_conn

def RunInSqliteThread(function):
  future = concurrentfuture.Future(function)
  SqliteExecutor().Add(future)
  try:
    return future.Wait()
  except concurrentfuture.FutureException, e:
    log("RunInSqliteThread failed:")
    log(e.message())
    raise


def Select(fields, table, where={}):
  def Curry():
    sql = "select %s from %s" % (",".join(fields), table)
    values = []
    c = Cursor()
    if where:
      clauses = []
      for k, v in where.iteritems():
        clauses.append("%s=?" % k)
        values.append(v)
      sql += " where %s" % " and ".join(clauses)
      log(sql,":",values)
      c.execute(sql, values)
    else:
      c.execute(sql)
    return RowsOf(c)
  return RunInSqliteThread(Curry)

def Update(table, values, where=None):
  def Curry():
    sql = "update %s set " % table
    list = []
    setvalues = []
    for k, v in values.iteritems():
      list.append("%s=?" % k)
      setvalues.append(v)
    sql += ",".join(list)

    c = Cursor()
    if where:
      list = []
      wherevalues = []
      sql += " where "
      for k, v in where.iteritems():
        list.append("%s=?" % k)
        wherevalues.append(v)
      sql += " and ".join(list)
      allvalues = setvalues + wherevalues
    else:
      allvalues = setvalues
    log(sql,allvalues)
    c.execute(sql, allvalues)
    SqliteConn().commit()
  return RunInSqliteThread(Curry)

def Insert(table, keysvalues):
  def Curry():
    sql = "insert into %s (" % table
    list = []
    setvalues = []
    for k, v in keysvalues.iteritems():
      list.append("%s" % k)
      setvalues.append(v)
    sql += ",".join(list)
    sql += ") values ("
    sql += ",".join(map(lambda x: "?", list))
    sql += ")"

    c = Cursor()
    log(sql, ":",setvalues)
    c.execute(sql, setvalues)
    SqliteConn().commit()
    return c.lastrowid
  return RunInSqliteThread(Curry)

def Remove(table, where):
  def Curry():
    sql = "remove from %s where " % table
    c = Cursor()
    if where:
      list = []
      wherevalues = []
      sql += " where "
      for k, v in where.iteritems():
        list.append("%s=?" % k)
        wherevalues.append(v)
      sql += " and ".join(list)
      allvalues = setvalues + wherevalues
    else:
      allvalues = setvalues
    log(sql,allvalues)
    c.execute(sql, allvalues)
    SqliteConn().commit()
  return RunInSqliteThread(Curry)

def Connect():
  global sqlite_conn
  sqlite_conn = sqlite3.connect(database_filename)
  sqlite_conn.row_factory = sqlite3.Row

def Cursor():
  return SqliteConn().cursor()

def RowGet(row, key, default):
  try:
    value = row[key]
  except KeyError:
    return default
  if value is None:
    return default
  else:
    return value

def PreInitSql(sql):
  global preinit_sql
  preinit_sql.append(sql)

def Sql(sql, optargs=None):
  def Curry():
    c = SqliteConn().cursor()
    if optargs:
      c.execute(sql, optargs)
    else:
      try:
        c.execute(sql)
      except:
        log("Exception while executing \"%s\"" % sql)
        raise
    return RowsOf(c)
  return RunInSqliteThread(Curry)

def Init():
  global sqlite_executor
  sqlite_executor = concurrentfuture.Executor("Sqlite")
  RunInSqliteThread(Connect)
  Sql("pragma synchronous = full")

  global preinit_sql
  for sql in preinit_sql:
    Sql(sql)

def Shutdown():
  future = SqliteExecutor().Exit()
  global sqlite_executor
  del sqlite_executor
  log("Waiting for executor to exit")
  future.Wait()
  log("Executor exited.")

